package com.soos.plugin.ds.spring.boot.autoconfigure;


import com.github.pagehelper.PageInterceptor;
import com.soos.plugin.ds.spring.boot.aop.DynamicDataSourceAop;
import com.soos.plugin.ds.spring.boot.enums.DBEnum;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.util.*;


@Component
@EnableConfigurationProperties(DynamicDataSourceProperties.class)
public class DynamicDataSourceAutoConfiguration {

    @Value("mybatis.mapper-locations")
    private String location;

    private final DynamicDataSourceProperties properties;

    public DynamicDataSourceAutoConfiguration(DynamicDataSourceProperties properties) {
        this.properties = properties;
    }


    @Bean("mainDataSource")
    @ConditionalOnClass(DynamicDataSourceProperties.class)
    public DataSource mainDataSource(DynamicDataSourceProperties properties) {
        return new HikariDataSource(properties.getMain());
    }

    @Bean("othersDataSource")
    @ConditionalOnClass(DynamicDataSourceProperties.class)
    public List<DataSource> othersDataSource(DynamicDataSourceProperties properties) {
        List<DataSource> others = new ArrayList<>();
        properties.getOthers().forEach(x -> others.add(new HikariDataSource(x)));
        return others;
    }

    @Bean("dynamicRoutingDataSource")
    @DependsOn({"mainDataSource", "othersDataSource"})
    public DataSource dataSource(@Qualifier("mainDataSource") DataSource mainDataSource,
                                 @Qualifier("othersDataSource") List<DataSource> othersDataSource) {
        DynamicRoutingDataSource source = new DynamicRoutingDataSource();
        Map<Object, Object> dataSourceMap = new HashMap<>(1 << 4);
        dataSourceMap.put(new DBInfo(DBEnum.MAIN, 0), mainDataSource);
        for (int i = 0; i < othersDataSource.size(); i++) {
            dataSourceMap.put(new DBInfo(DBEnum.OTHERS, i), othersDataSource.get(i));
        }
        source.setDefaultTargetDataSource(mainDataSource);
        source.setTargetDataSources(dataSourceMap);
        return source;
    }

    @Bean("sqlSessionFactory")
    private SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicRoutingDataSource") DataSource dataSource,
                                                @Qualifier("pageInterceptor") Interceptor pageInterceptor) {
        SqlSessionFactory sqlSessionFactory = null;
        try {
            SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
            // 设置mapper.xml的文件路径 getResources
            factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(location));
            // 设置动态数据库
            factoryBean.setDataSource(dataSource);
            // 设置分页插件
            factoryBean.setPlugins(pageInterceptor);
            sqlSessionFactory = factoryBean.getObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sqlSessionFactory;
    }

    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager(@Qualifier("dynamicRoutingDataSource") DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }

    /**
     * @return PageInterceptor
     * @author SOOS
     * @desc 创建分页插件
     * @date 2022/3/4 22:28
     */
    @Bean(name = "pageInterceptor")
    public PageInterceptor pageInterceptor() {
        PageInterceptor interceptor = new PageInterceptor();
        Properties property = new Properties();
        // 设置分页插件的配置 pageHelper的官网配置说明
        // 如果你在 Spring 中配置了动态数据源，并且连接不同类型的数据库，
        // 这时你可以配置 autoRuntimeDialect 为 true，
        // 这样在使用不同数据源时，会使用匹配的分页进行查询。
        // 这种情况下，你还需要特别注意 closeConn 参数，
        // 由于获取数据源类型会获取一个数据库连接，所以需要通过这个参数来控制获取连接后，
        // 是否关闭该连接。 默认为 true，有些数据库连接关闭后就没法进行后续的数据库操作。
        // 而有些数据库连接不关闭就会很快由于连接数用完而导致数据库无响应。
        // 所以在使用该功能时，特别需要注意你使用的数据源是否需要关闭数据库连接。
        // 当不使用动态数据源而只是自动获取 helperDialect 时，
        //  数据库连接只会获取一次，所以不需要担心占用的这一个连接是否会导致数据库出错，但是最好也根据数据源的特性选择是否关闭连接。
        property.put("autoRuntimeDialect", "true");
        // 分页合理化参数，默认值为false。当该参数设置为 true 时，pageNum<=0 时会查询第一页，
        // pageNum>pages（超过总数时），会查询最后一页。默认false 时，直接根据参数进行查询。
        property.put("reasonable", "true");
        interceptor.setProperties(property);
        return interceptor;
    }

    /**
     * @return DynamicDataSourceAop
     * @author SOOS
     * @desc 创建数据库aop切面
     * @date 2022/3/4 22:28
     */
    @Bean
    public DynamicDataSourceAop dynamicDataSourceAop(){
        return new DynamicDataSourceAop();
    }


}
